﻿#include <iostream>
#include <string>
#include <assert.h>
#include "core.h"
#include "Shader.h"
#include "checkError.h"
#include "Application.h"

GLuint vao;
Shader* shader = nullptr;

// 声明且实现窗体变化回调
void OnResze(int width, int height)
{
  std::cout << "窗体最新大小：" << width << " " << height << std::endl;
  GL_CALL(glViewport(0, 0, width, height));
}

void OnKey(int key, int scancode, int action, int mods)
{
  if (action == GLFW_PRESS)
  {
    switch (key)
    {
    case GLFW_KEY_M:
    {
      static GLenum  mode = GL_FILL;

      // 填充或轮廓
      mode = (mode == GL_FILL ? GL_LINE : GL_FILL);
      // 多边形光栅化模式
      glPolygonMode(GL_FRONT_AND_BACK, mode);
    }
    }
  }
}


void prepareSingleBuffer()
{
  float positions[] = {
    -0.5f, -0.5f, 0.0f,
     0.5f, -0.5f, 0.0f,
     0.0f,  0.5f, 0.0f
  };

  float colors[] = {
    1.0f, 0.0f, 0.0f,
    0.0f, 1.0f, 0.0f,
    0.0f,  0.0f, 1.0f
  };
  // 1 生成一个vbo
  GLuint vboPos = 0, vboColor = 0;
  GL_CALL(glGenBuffers(1, &vboPos));
  GL_CALL(glGenBuffers(1, &vboColor));

  // 2 绑定当前vbo，到opengl状态机的当前vbo插槽
  GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vboPos));
  GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW));

  GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vboColor));
  GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW));

  // 创建及绑定vao
  GLuint vaoPos = 0;
  GL_CALL(glGenVertexArrays(1, &vaoPos));
  GL_CALL(glBindVertexArray(vaoPos));
  // 描述为指数型
  GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vboPos)); // 只有绑定了vbo，下面的属性描述才会有当前vbo有关系
  GL_CALL(glEnableVertexAttribArray(0));
  GL_CALL(glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GL_FLOAT), (GLvoid*)0));

  // 创建及绑定vao
  GLuint vaoColor = 0;
  GL_CALL(glGenVertexArrays(1, &vaoColor));
  GL_CALL(glBindVertexArray(vaoColor));
  // 描述为指数型
  GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vboColor)); // 只有绑定了vbo，下面的属性描述才会有当前vbo有关系
  GL_CALL(glEnableVertexAttribArray(1));
  GL_CALL(glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (GLvoid*)0));

  GL_CALL(glBindVertexArray(0));
}

void prepareInterleavedBuffer()
{
  float vertices[] = {
    -0.5f, -0.5f, 0.0f,1.0f, 0.0f, 0.0f,
     0.5f, -0.5f, 0.0f,0.0f, 1.0f, 0.0f,
     0.0f,  0.5f, 0.0f,0.0f,  0.0f, 1.0f
  };

  GLuint vbo = 0;
  GL_CALL(glGenBuffers(1, &vbo));
  GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vbo));
  GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW));

  GL_CALL(glGenVertexArrays(1, &vao));
  GL_CALL(glBindVertexArray(vao));

  //GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vbo));

  GL_CALL(glEnableVertexAttribArray(0));
  GL_CALL(glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GL_FLOAT), (void*)0));

  GL_CALL(glEnableVertexAttribArray(1));
  GL_CALL(glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GL_FLOAT), (void*)(3 * sizeof(GL_FLOAT))));

  GL_CALL(glBindVertexArray(0));
}

void prepareVAOForTriangles()
{
  float positions[] = {
    -0.5f, -0.5f, 0.0f,
     0.5f, -0.5f, 0.0f,
     0.0f,  0.5f, 0.0f,
     0.5f,  0.5f, 0.0f,
     0.8f,  0.8f, 0.0f,
     0.8f,  0.0f, 0.0f
  };

  GLuint posVbo;
  glGenBuffers(1, &posVbo);
  glBindBuffer(GL_ARRAY_BUFFER, posVbo);
  glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);

  glGenVertexArrays(1, &vao);
  glBindVertexArray(vao);

  glEnableVertexAttribArray(0);
  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GL_FLOAT), (void*)0);

  // 描述完属性就可以解除绑定了
  glBindVertexArray(0);
}

void prepareVAO()
{
  float positions[] = {
  -0.5f, -0.5f, 0.0f,
   0.5f, -0.5f, 0.0f,
   0.0f,  0.5f, 0.0f
  };

  float colors[] = {
    1.0f, 0.0f, 0.0f,
    0.0f, 1.0f, 0.0f,
    0.0f, 0.0f, 1.0f
  };

  unsigned int indices[] = {
    0,1,2
  };

  // 2 VBO创建
  GLuint posVbo;
  glGenBuffers(1, &posVbo);
  glBindBuffer(GL_ARRAY_BUFFER, posVbo);
  glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);

  GLuint colorVbo;
  glGenBuffers(1, &colorVbo);
  glBindBuffer(GL_ARRAY_BUFFER, colorVbo);
  glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);

  // 3 ebo创建
  GLuint ebo;
  glGenBuffers(1, &ebo);
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
  glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

  // 4 vao创建
  glGenVertexArrays(1, &vao);
  glBindVertexArray(vao);

  // 5 绑定vbo、ebo加入属性和描述
  // 5.1 加入位置属性描述信息
  glBindBuffer(GL_ARRAY_BUFFER, posVbo);
  glEnableVertexAttribArray(0);
  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(GL_FLOAT) * 3, (void*)0);

  // 5.1 加入位置属性描述信息
  glBindBuffer(GL_ARRAY_BUFFER, colorVbo);
  glEnableVertexAttribArray(1);
  glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(GL_FLOAT) * 3, (void*)0);

  // 5.3 加入ebo到当前的vao（这句在上面虽然已经有了，但是上面未与vao产生关联）
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);

  // 解绑vao了
  glBindVertexArray(0);
}

void prepareShader()
{
  shader = new Shader("vertex.glsl", "fragment.glsl");
}

void render()
{
  // 如果有这一行，则第二次循环会崩溃，因为第一次还没绑定vao，这个对vao没影响，氮循环一次后vao被绑定了，再执行这行会将vao中的ebo清理掉了，导致draw时崩溃。处理办法是循环最后加入glBindVertexArray(0)。opengl是个状态机。
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
  // 执行opengl画布清理操作
  glClear(GL_COLOR_BUFFER_BIT);
  // 绑定program
  shader->begin();
  // 绑定vao
  glBindVertexArray(vao);

  // 发出绘制命令
  // glDrawArrays(GL_TRIANGLES, 0, 3);
  // glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);
  glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, (void*)0);
  // glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, (void*)(sizeof(GL_UNSIGNED_INT) * 3));

  glBindVertexArray(0);

  shader->end();
}

int main()
{

  if (!app->init(800, 600))
  {
    return -1;
  }

  // 设置窗口回调
  app->setResizeCallback(OnResze);
  app->setKeyboardCallback(OnKey);

  /* 设置opengl视口以及清理颜色 */
  GL_CALL(glViewport(0, 0, 800, 600));
  GL_CALL(glClearColor(0.2f, 0.3f, 0.3f, 1.0f));

  prepareShader();
  prepareVAO();

  // 3 执行窗体循环
  while (app->update())
  {
    render();
  }

  // 4 退出程序前清理
  app->destroy();
  return 0;
}